package com.linb.pan.core.utils;

import cn.hutool.core.date.DateUtil;
import com.linb.pan.core.constants.BPanConstants;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.unit.DataUnit;

import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;

public class FileUtil {

    /**
     * 获取文件后缀
     */
    public static String getFileSuffix(String filename) {
        //不能为空，必须包含.(点)
        if (StringUtils.isBlank(filename) || filename.indexOf(BPanConstants.POINT_STR) == BPanConstants.MINUS_ONE_INT) {
            return StringUtils.EMPTY;
        }
        return filename.substring(filename.lastIndexOf(BPanConstants.POINT_STR));
    }

    /**
     * 获取文件拓展名
     */
    public static String getFileExtName(String filename) {
        //不能为空，必须包含.(点)
        if (StringUtils.isBlank(filename) || filename.indexOf(BPanConstants.POINT_STR) == BPanConstants.MINUS_ONE_INT) {
            return StringUtils.EMPTY;
        }
        return filename.substring(filename.lastIndexOf(BPanConstants.POINT_STR) + 1).toLowerCase();
    }

    /**
     * 文件大小转描述 如1024->1MB
     */
    public static String byteCountToDisplaySize(Long totalSize) {
        if (totalSize == null) {
            return BPanConstants.EMPTY_STR;
        }
        return FileUtils.byteCountToDisplaySize(totalSize);
    }


    /**
     * 批量删除文件
     */
    public static void deleteFiles(List<String> realFilePathList) throws IOException {
        if (CollectionUtils.isEmpty(realFilePathList)) {
            return;
        }
        for (String realFilePath : realFilePathList) {
            FileUtils.forceDelete(new File(realFilePath));
        }
    }

    /**
     * 生成真实路径
     */
    public static String generateStoreFileRealPath(String basePath, String filename) {
        return new StringBuffer(basePath)
                .append(File.separator)
                .append(DateUtil.thisYear())
                .append(File.separator)
                .append(DateUtil.thisMonth() + 1)
                .append(File.separator)
                .append(DateUtil.thisDayOfMonth())
                .append(File.separator)
                .append(UUIDUtil.getUUID())
                .append(getFileSuffix(filename))
                .toString();
    }

    /**
     * 将文件的输入流写入到文件中
     */
    public static void writeStreamToFile(InputStream inputStream, File targetFile, Long totalSize) throws IOException {
        createFile(targetFile);
        //零拷贝
        RandomAccessFile randomAccessFile = new RandomAccessFile(targetFile, "rw");
        FileChannel outputChannel = randomAccessFile.getChannel();
        ReadableByteChannel inputChannel = Channels.newChannel(inputStream);
        outputChannel.transferFrom(inputChannel, 0L, totalSize);

        inputStream.close();
        randomAccessFile.close();
        outputChannel.close();
        inputChannel.close();
    }

    /**
     * 生成默认文件存储路径
     *
     * @return
     */
    public static String generateDefaultStoreFileRealPath() {
        return new StringBuffer("E:\\data")
                .append(File.separator)
                .append("bpan")
                .toString();
    }

    /*===================================private===================================*/

    /**
     * 创建文件
     */
    public static void createFile(File targetFile) throws IOException {
        //1.没有文件夹就创建文件夹
        if (!targetFile.getParentFile().exists()) {
            targetFile.getParentFile().mkdirs();
        }
        //2.创建文件
        targetFile.createNewFile();
    }

    /**
     * 分片文件存储路径前缀
     */
    public static String generateDefaultStoreFileChunkRealPath() {
        return new StringBuffer("E:\\data")
                .append(File.separator)
                .append("bpan")
                .append(File.separator)
                .append("chunks")
                .toString();
    }

    /**
     * 分片文件存储真实路径
     */
    public static String generateStoreFileChunkRealPath(String basePath, String identifier, Integer chunkNumber) {
        return new StringBuffer(basePath)
                .append(File.separator)
                .append(DateUtil.thisYear())
                .append(File.separator)
                .append(DateUtil.thisMonth() + 1)
                .append(File.separator)
                .append(DateUtil.thisDayOfMonth())
                .append(File.separator)
                .append(identifier)
                .append(File.separator)
                .append(UUIDUtil.getUUID())
                .append(BPanConstants.COMMON_SEPARATOR)
                .append(chunkNumber)
                .toString();
    }

    /**
     * 追加写
     */
    public static void appendWrite(Path target, Path source) throws IOException {
        Files.write(target, Files.readAllBytes(source), StandardOpenOption.APPEND);
    }

    /**
     * 零拷贝技术读取文件内容并写入到输出流中
     */
    public static void writeFileOutputStream(FileInputStream fileInputStream, OutputStream outputStream, long length) throws IOException {
        FileChannel fileChannel = fileInputStream.getChannel();
        WritableByteChannel writableByteChannel = Channels.newChannel(outputStream);
        fileChannel.transferTo(BPanConstants.ZERO_LONG, length, writableByteChannel);
        outputStream.flush();
        fileInputStream.close();
        outputStream.close();
        fileChannel.close();
        writableByteChannel.close();
    }

    /**
     * 普通的流对流数据传输
     */
    public static void writeStream2StreamNormal(InputStream inputStream, OutputStream outputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, len);
        }
        outputStream.flush();
        inputStream.close();
        outputStream.close();
    }
}
